package com.google.code;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils;

/**
 * @author Leopoldo Muller
 */
public class BunitBeanPostProcessor implements BeanPostProcessor {

    @Autowired
    private BeanFactory factory;

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object,
     * java.lang.String)
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        for (Field f : bean.getClass().getDeclaredFields()) {

            if (f.isAnnotationPresent(Given.class) && !Modifier.isFinal(f.getModifiers())) {

                inject(bean, f);
            }
        }

        return bean;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object,
     * java.lang.String)
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        return bean;
    }

    /**
     * Get value of bean named {@link Given#value()} and set it on {@code field} of {@code bean}.
     * 
     * @param bean
     *            bean containing {@code field} to set new value
     * 
     * @param field
     *            field annotated with {@link Given}
     */
    void inject(Object bean, Field field) {

        try {

            ReflectionUtils.makeAccessible(field);

            field.set(bean, factory.getBean(field.getAnnotation(Given.class).value()));

        } catch (Exception e) {

            throw new BeanCreationException("Could not inject field: " + field, e);
        }
    }
}
